package com.stars.easyms.rest.handler;

import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.http.EasyMsRequestEntity;
import com.stars.easyms.base.http.EasyMsRequestSynchronizationManager;
import com.stars.easyms.base.util.DateTimeUtil;
import com.stars.easyms.base.util.PatternMatcherUtil;
import com.stars.easyms.base.util.SerialNumberUtil;
import com.stars.easyms.base.util.SpringBootUtil;
import com.stars.easyms.redis.template.EasyMsRedisTemplate;
import com.stars.easyms.rest.constant.RestConstants;
import com.stars.easyms.rest.exception.RestDuplicateRequestException;
import com.stars.easyms.rest.properties.EasyMsRestProperties;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * <p>className: EasyMsRequestHandler</p>
 * <p>description: EasyMs的请求处理器</p>
 *
 * @author guoguifang
 * @version 1.6.2
 * @date 2020/9/5 2:01 下午
 */
public class EasyMsRequestHandler {

    private static final Logger logger = LoggerFactory.getLogger(EasyMsRequestHandler.class);

    private EasyMsRedisTemplate easyMsRedisTemplate;

    private EasyMsRestProperties easyMsRestProperties;

    /**
     * 解析request头，不允许抛出异常
     */
    @Nullable
    public EasyMsRequestEntity parseRequestHeader(HttpServletRequest servletRequest) {

        // 获取接收请求时间
        long receiveRequestTime = System.currentTimeMillis();
        String receiveRequestTimeStr = DateTimeUtil.getDatetimeNormalStrWithMills(receiveRequestTime);

        // 获取请求地址
        String requestPath = servletRequest.getRequestURI().substring(servletRequest.getContextPath().length());

        // 校验请求地址，如果是easy-ms相关的url则直接返回null
        for (String permitUrl : HttpHeaderConstants.EASY_MS_URL) {
            if (PatternMatcherUtil.doUrlPatternMatch(permitUrl, requestPath, true)) {
                return null;
            }
        }

        // 获取全局链路ID以及请求ID
        String traceId = servletRequest.getHeader(HttpHeaderConstants.TRACE_KEY);
        String requestId = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_ID);

        // 获取请求系统，如果header头中不存在则使用unknown
        String requestSys = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS);
        if (StringUtils.isBlank(requestSys)) {
            requestSys = HttpHeaderConstants.UNKNOWN_SYS;
        }

        // 获取header头中的请求时间
        String requestTime = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_TIME);

        // 如果开启了请求去重则判断requestId是否已经存在
        if (easyMsRedisTemplate != null && easyMsRestProperties.isDuplicateRemovalEnabled() && StringUtils.isNotBlank(requestId)) {
            String redisKey = easyMsRedisTemplate.getRedisKeyWithPrefix(RestConstants.REDIS_DUPLICATE_REQUEST_KEY, requestPath, requestId);
            if (!easyMsRedisTemplate.setIfAbsent(redisKey, receiveRequestTime, 10, TimeUnit.MINUTES)) {
                String firstRequestTimeStr = easyMsRedisTemplate.get(redisKey);
                if (StringUtils.isNotBlank(firstRequestTimeStr)) {
                    long firstRequestTime = Long.parseLong(firstRequestTimeStr);
                    throw new RestDuplicateRequestException("[捕获一个重复请求]-[请求地址:{}]-[请求系统:{}]-[服务系统:{}]-[链路ID:{}]-" +
                            "[请求ID:{}]-[请求时间:{}]-[首次接收请求时间:{}]-[接收请求时间:{}].",
                            requestPath, requestSys, SpringBootUtil.getApplicationName(), traceId, requestId, withUnknown(requestTime),
                            DateTimeUtil.getDatetimeNormalStrWithMills(firstRequestTime), receiveRequestTimeStr);
                }
            }
        }

        // 如果是空的则设置一个默认值
        if (StringUtils.isBlank(traceId)) {
            traceId = SerialNumberUtil.getSerialNumber();
        }
        if (StringUtils.isBlank(requestId)) {
            requestId = SerialNumberUtil.getSerialNumber();
        }

        // 将traceId和requestId放入日志线程本地变量中
        EasyMsRequestSynchronizationManager.setTraceInfo(traceId, requestId);

        // 解析请求头中的用户信息
        String userInfoStr = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_USER_INFO);
        String decodedUserInfoStr = userInfoStr;
        if (StringUtils.isNotBlank(userInfoStr)) {
            decodedUserInfoStr = new String(Base64.decodeBase64(userInfoStr), StandardCharsets.UTF_8);
            EasyMsRequestSynchronizationManager.setUserInfo(userInfoStr, decodedUserInfoStr);
        }

        // 记录接收服务日志信息并抛出异常
        logger.info("[接收服务-请求]-[请求地址:{}]-[请求系统:{}]-[服务系统:{}]-[请求ID:{}]-[请求时间:{}]-[接收请求时间:{}]{}.",
                requestPath, requestSys, SpringBootUtil.getApplicationName(), requestId, withUnknown(requestTime), receiveRequestTimeStr,
                StringUtils.isNotBlank(decodedUserInfoStr) ? "-[请求用户信息:" + decodedUserInfoStr + "]" : "");

        // 封装请求参数
        EasyMsRequestEntity requestEntity = new EasyMsRequestEntity();
        requestEntity.setRequestPath(requestPath);
        requestEntity.setTraceId(traceId);
        requestEntity.setRequestSys(requestSys);
        requestEntity.setRequestId(requestId);
        requestEntity.setRequestTime(requestTime);
        requestEntity.setReceiveRequestTime(receiveRequestTimeStr);

        // 将requestEntity放入本地变量中
        EasyMsRequestSynchronizationManager.setRequestEntity(requestEntity);

        return requestEntity;
    }

    public void clearRequestSynchronization() {
        EasyMsRequestSynchronizationManager.clear();
    }

    public EasyMsRequestHandler(EasyMsRestProperties easyMsRestProperties, EasyMsRedisTemplate easyMsRedisTemplate) {
        this.easyMsRestProperties = easyMsRestProperties;
        this.easyMsRedisTemplate = easyMsRedisTemplate;
    }

    private String withUnknown(String str) {
        return StringUtils.isBlank(str) ? "unknown" : str;
    }
}
